/*
 * Description: 应用时长打点
 */
#include "app_using_report.h"
#include "log.h"
#include <winver.h>
#include <tchar.h>
#include "text_util/string_proc.h"
#include "common/guest_utils.h"
#include "common/session_utils.h"
#include "process_load_statistic/perf_counter.h"

using namespace COMMON;

namespace {
    const int COLLECT_SYSLOAD_INTERVAL_MS = 2000; // 采集系统负载信息间隔2000ms
    const int REPORT_INTERVAL_SECONDS = 300; // 打点上报间隔5min

    const size_t OUTPUT_PROCESS_CPU_USAGE_NUM = 50; // 上报进程CPU top 50
    const size_t OUTPUT_PROCESS_MEM_NUM = 5;  // 上报进程MEM top 5
    const size_t OUTPUT_PROCESS_PAGE_NUM = 5;  // 上报进程PAGE top 5

    const int TOP_CPU_UPLOAD_THRESHOLD = 1; // CPU top5打点上报阈值
    const int TOP_MEM_UPLOAD_THRESHOLD = 1; // // 物理内存TOP5打点上报阈值
    const int TOP_PAGE_UPLOAD_THRESHOLD = 1; // // 提交内存TOP5打点上报阈值

    const int ONE_HUNDRED_PERCENT = 100;
    const int BYTES_TO_KB = 1024;

    constexpr auto PIPE_NAME = R"(\\.\PIPE\WIN_EVENT)";
    constexpr auto BUFSIZE = 512;
    constexpr int MAX_WAIT_MONITOR_TIMEOUT_MS = 500;
    constexpr int RE_CREATE_NAMEDPIPE_INTERVAL_MS = 3000;

    const std::string WIN_EVT_MONITOR_PATH = "C:\\ProgramData\\WinEvtMonitor\\win_evt_monitor.exe";
    const std::string WIN_EVT_MONITOR_NAME = "win_evt_monitor.exe";

    constexpr float DISCOUNT_BETWEEN_DAY_AND_MILLISECOND = 86400000.0;
    const int POWER_STATE_UNKNOW = 255;
}

namespace DFX {
void AppUsingReport::Init(std::shared_ptr<WINCONTROL::SendToHost> sendtoHost)
{
    if (sendtoHost == nullptr) {
        LOG_ERR("[DFX - AppUsingReport] - sendtoHost is null!");
    }
    m_sendtoHost = sendtoHost;
    SessionUtils::RegSessionChangeCallback(sessionName,
        std::bind(&AppUsingReport::SessionChanged, &APP_USING_REPORT, std::placeholders::_1));

    isShutDown = false;
}

void AppUsingReport::Deinit()
{
    if (m_sendtoHost != nullptr) {
        m_sendtoHost.reset();
    }
    SessionUtils::UnRegSessionChangeCallback(sessionName);
}

void AppUsingReport::SessionChanged(int32_t state)
{
    if (state == WTS_SESSION_LOGON) {
        // 判断如果win_evt_monitor没在，拉起
        if (!GuestUtils::IsProcessRunning(WIN_EVT_MONITOR_NAME)) {
            LOG_INFO("[DFX - AppUsingReport] - win_evt_monitor not exist, call it");
            (void)GuestUtils::ExecCmd(WIN_EVT_MONITOR_PATH, true, false);
        }
    }
}

void AppUsingReport::Start()
{
    LOG_INFO("[DFX - AppUsingReport] - Start");
    if (appUsingReportThread.joinable()) {
        appUsingReportThread.join();
    }
    appUsingReportThread = std::thread([this]() {
        if (this != nullptr) {
            stopReportFlag = false;
            ReportPerfData();
        }
    });
    if (sysCpuCalcThread.joinable()) {
        sysCpuCalcThread.join();
    }
    sysCpuCalcThread = std::thread([this]() {
        if (this != nullptr) {
            stopCollectFlag = false;
            CollectSysLoadData();
        }
    });
    if (monitorSysWndThread.joinable()) {
        monitorSysWndThread.join();
    }
    monitorSysWndThread = std::thread([this]() {
        if (this != nullptr) {
            stopMonitorWndEvtFlag = false;
            while (!stopMonitorWndEvtFlag && !isShutDown) {
                MonitorWinEvent();
                if (!stopMonitorWndEvtFlag && !isShutDown) {
                    std::this_thread::sleep_for(std::chrono::milliseconds(RE_CREATE_NAMEDPIPE_INTERVAL_MS));
                }
            }
        }
    });
}

void AppUsingReport::Stop()
{
    LOG_INFO("[DFX - AppUsingReport] - Stop");
    {
        std::lock_guard<std::mutex> lck(reportMtx);
        stopReportFlag = true;
    }
    {
        std::lock_guard<std::mutex> lck(collectMtx);
        stopCollectFlag = true;
    }
    reportCV.notify_one();
    collectCV.notify_one();
    if (appUsingReportThread.joinable()) {
        appUsingReportThread.join();
    }
    if (sysCpuCalcThread.joinable()) {
        sysCpuCalcThread.join();
    }
    stopMonitorWndEvtFlag = true;
    if (monitorSysWndThread.joinable()) {
        if (!isWinEvtMonitorConnected) {
            LOG_INFO("[DFX - AppUsingReport] Interupt ConnectNamedPipe manually");
            SetEvent(connOverLapped.hEvent);
            SAFE_CLOSE_HANDLE(connOverLapped.hEvent, INVALID_HANDLE_VALUE);
        }
        if (hPipe != INVALID_HANDLE_VALUE) {
            LOG_INFO("[DFX - AppUsingReport] Interupt recv msg manually");
            BOOL ret = CancelIoEx(hPipe, nullptr);
            if (!ret) {
                LOG_ERR("[DFX - AppUsingReport] Interupt recv msg manually failed, err: {}", GetLastError());
            }
        }
        monitorSysWndThread.join();
    }
    LOG_INFO("[DFX - AppUsingReport] - Stop finished");
}

void AppUsingReport::SetShutdownFlag()
{
    isShutDown = true;
}

void AppUsingReport::ReportPerfData()
{
    std::unique_lock<std::mutex> lck(reportMtx);
    while (!stopReportFlag) {
        while (!reportCV.wait_for(lck, std::chrono::seconds(REPORT_INTERVAL_SECONDS),
            [this]() { return (this == nullptr || stopReportFlag);})) {
            if (!SessionUtils::IsUserLogon()) {
                LOG_INFO("[DFX - AppUsingReport] - no user logon, ignore");
                continue;
            }
            PerfDataContainer perfData;
            FillPerfData(perfData);
        }
        LOG_INFO("[DFX - AppUsingReport] report loop break");
    }
}

void AppUsingReport::CollectSysLoadData()
{
    std::unique_lock<std::mutex> lck(collectMtx);
    while (!stopCollectFlag) {
        while (!collectCV.wait_for(lck, std::chrono::milliseconds(COLLECT_SYSLOAD_INTERVAL_MS),
            [this]() { return (this == nullptr || stopCollectFlag);})) {
            SysCpuCalc::GetInstance().CollectProcLoad();
        }
        LOG_INFO("[DFX - AppUsingReport] collect loop break");
    }
}

void AppUsingReport::FillPerfData(PerfDataContainer& perfData)
{
    std::map<DWORD, SystemProcessItem> processinfo = SysCpuCalc::GetInstance().GetProcessInfos();
    // 系统类
    GetSystemInfo(perfData.systemInfo);
    // 当前系统CPU使用率TOP 10
    GetTopProcessCpuInfo(perfData);
    // 当前进程MEM使用TOP 5
    GetTopProcessMemInfo(perfData, OUTPUT_PROCESS_MEM_NUM, processinfo);
    // 当前进程PAGE使用TOP 5
    GetTopProcessPageInfo(perfData, OUTPUT_PROCESS_PAGE_NUM, processinfo);
    // 打点上报
    RecordBiLog(perfData);
}

void AppUsingReport::GetTopProcessCpuInfo(PerfDataContainer& perfData)
{
    std::map<DWORD, SystemProcessItem> procCpuUsage = SysCpuCalc::GetInstance().GetFilterProcessInfos();
    if (procCpuUsage.empty()) {
        return;
    }
    // 排序
    std::vector<std::pair<DWORD, SystemProcessItem>> currentProcCpuInfo(procCpuUsage.begin(), procCpuUsage.end());
    std::sort(currentProcCpuInfo.begin(), currentProcCpuInfo.end(),
        [](const auto& procInfoLeft, const auto& procInfoRight) {
        return procInfoLeft.second.cpuUsage > procInfoRight.second.cpuUsage;
    });
    std::vector<std::pair<std::string, std::pair<float, std::string>>> topProcCpuUsage;
    for (size_t i = 0; i < currentProcCpuInfo.size() && i < OUTPUT_PROCESS_CPU_USAGE_NUM; ++i) {
        if ((currentProcCpuInfo[i].second.cpuUsage * HUNDRED_CPU_USAGE) >= TOP_CPU_UPLOAD_THRESHOLD) {
            // 加入应用版本号
            std::string version = "";
            (void)GetFileVersion(currentProcCpuInfo[i].second.fullProcessName, version);
            topProcCpuUsage.push_back(std::pair<std::string, std::pair<float, std::string>>(
                currentProcCpuInfo[i].second.processName,
                {currentProcCpuInfo[i].second.cpuUsage * HUNDRED_CPU_USAGE, version}));
        }
    }
    perfData.vecTopProcCpuUsage = topProcCpuUsage;
}

void AppUsingReport::GetTopProcessMemInfo(PerfDataContainer& perfData, const int topN,
    std::map<DWORD, SystemProcessItem>& processinfo)
{
    std::vector<std::pair<std::string, DWORDLONG>> vecProcMem = {};
    DWORDLONG memThreshold = perfData.systemInfo.totalPhyMem * TOP_MEM_UPLOAD_THRESHOLD / ONE_HUNDRED_PERCENT;
    for (auto inter = processinfo.begin(); inter != processinfo.end(); inter++) {
        std::pair<std::string, DWORDLONG> nowProc;
        nowProc.first = inter->second.processName;
        nowProc.second = (inter->second.mem);
        if (nowProc.second >= memThreshold) {
            vecProcMem.push_back(nowProc);
        }
    }
    if (vecProcMem.empty()) {
        return;
    }
    std::sort(vecProcMem.begin(), vecProcMem.end(),
        [](const auto& procInfoLeft, const auto& procInfoRight) {
        return procInfoLeft.second > procInfoRight.second;
    });
    for (size_t i = 0; i < vecProcMem.size() && i < topN; ++i) {
        perfData.vecTopProcMem.push_back(vecProcMem[i]);
    }
}

void AppUsingReport::GetTopProcessPageInfo(PerfDataContainer& perfData, const int topN,
    std::map<DWORD, SystemProcessItem>& processinfo)
{
    std::vector<std::pair<std::string, DWORDLONG>> vecProcPage = {};
    DWORDLONG pageThreshold = perfData.systemInfo.totalPageFile * TOP_PAGE_UPLOAD_THRESHOLD / ONE_HUNDRED_PERCENT;
    for (auto inter = processinfo.begin(); inter != processinfo.end(); inter++) {
        std::pair<std::string, DWORDLONG> nowProc;
        nowProc.first = inter->second.processName;
        nowProc.second = (inter->second.vmem);
        if (nowProc.second >= pageThreshold) {
            vecProcPage.push_back(nowProc);
        }
    }
    if (vecProcPage.empty()) {
        return;
    }
    std::sort(vecProcPage.begin(), vecProcPage.end(),
        [](const auto& procInfoLeft, const auto& procInfoRight) {
        return procInfoLeft.second > procInfoRight.second;
    });
    for (size_t i = 0; i < vecProcPage.size() && i < topN; ++i) {
        perfData.vecTopProcPage.push_back(vecProcPage[i]);
    }
}

void AppUsingReport::RecordBiLog(PerfDataContainer& perfData)
{
    // 进程类
    GetProcessInfo(perfData.vecApplicationInfo);
    std::string displayPerfData;
    std::ostringstream displayPerfDataStream;
    // APP信息
    displayPerfDataStream << "{\"AppInfo\":[";
    FormatAppInfo(perfData, displayPerfDataStream);
    displayPerfData = displayPerfDataStream.str();
    if (displayPerfData.size() >= sizeof(char) && displayPerfData[displayPerfData.size() - sizeof(char)] == ',') {
        displayPerfData.erase(displayPerfData.size() - sizeof(char), sizeof(char));
    }
    displayPerfDataStream.str("");
    displayPerfDataStream << displayPerfData << "]";
    // systemInfo 信息
    FormatSystemInfo(perfData, displayPerfDataStream);
    // Process CPU top N
    std::string topApp;
    FormatAppCpuTopN(perfData.vecTopProcCpuUsage, topApp);
    displayPerfDataStream << topApp;
    // Process Mem top N
    std::string topProcMemInfo;
    AppMemTopN(perfData, topProcMemInfo);
    displayPerfDataStream << topProcMemInfo;
    // Process Page top N
    std::string topProcPageInfo;
    AppPageTopN(perfData, topProcPageInfo);
    displayPerfDataStream << topProcPageInfo;
    displayPerfDataStream << "}";
    displayPerfData = displayPerfDataStream.str();
    // 上报
    LOG_INFO("[DFX - AppUsingReport] report data: {}", displayPerfData.c_str());
    if (!m_sendtoHost) {
        LOG_ERR("[DFX - AppUsingReport] sendtoHost is null!");
        return;
    }
}

void AppUsingReport::GetProcessInfo(std::vector<ApplicationInfo>& vecAppInfo)
{
    std::vector<ProcessLifeRecord> processInfo = PROCESSDETECT.GetProcessLifeInfo();
    auto mapProcessInfo = SysCpuCalc::GetInstance().GetProcessInfos();
    for (auto proc : processInfo) { // 对白名单里的每一个进程
        auto itr = mapProcessInfo.find(proc.processId); // 去sysperf里面找,因为sysperf记录了进程的cpu\mem等负载信息
        if (itr != mapProcessInfo.end()) {
            if (StringProc::StringToWString(itr->second.processName) == proc.processName) {
                GetReportAppData(proc, itr->second, vecAppInfo);
            } else {
                // 虽然找到了进程, 但名字不一样, 说明已经关闭
                GetReportAppDataAfterProcessHaveClosed(proc, vecAppInfo);
            }
        } else {
            // 进程已经关闭了
            GetReportAppDataAfterProcessHaveClosed(proc, vecAppInfo);
        }
    }
}

void AppUsingReport::GetReportAppData(const ProcessLifeRecord& procLifeRecord,
    const SystemProcessItem& procInfo, std::vector<ApplicationInfo>& vecAppInfo)
{
    ApplicationInfo appInfo;
    appInfo.appCpuRate = static_cast<float>(procInfo.cpuUsage * ONE_HUNDRED_PERCENT);
    appInfo.appDiskReadSpeepd = static_cast<float>(procInfo.diskReadPerSec);
    appInfo.appDiskWriteSpeepd = static_cast<float>(procInfo.diskWritePerSec);
    appInfo.appIsFocused = false; // 暂不实现
    appInfo.appIsFullScreen = false; // 暂不实现
    appInfo.appIsSemiFullScreen = 0;
    appInfo.appLaunchTime = procLifeRecord.processCreateTime;
    appInfo.appMemory = static_cast<int>(procInfo.mem);
    appInfo.appPageFile = static_cast<int>(procInfo.vmem);
    appInfo.appName = StringProc::StringToWStringByUTF8(procInfo.processName);
    appInfo.appQuitTime = procLifeRecord.processCloseTime;
    std::string version;
    (void)GetFileVersion(procInfo.fullProcessName, version);
    appInfo.appVersion = version;
    vecAppInfo.push_back(appInfo);
}

void AppUsingReport::GetReportAppDataAfterProcessHaveClosed(const ProcessLifeRecord& procLifeRecord,
    std::vector<ApplicationInfo>& vecAppInfo)
{
    ApplicationInfo appInfo;
    appInfo.appCpuRate = 0;
    appInfo.appDiskReadSpeepd = 0;
    appInfo.appDiskWriteSpeepd = 0;
    bool appIsFocused = false;
    appInfo.appIsFocused = appIsFocused;
    appInfo.appIsFullScreen = 0;
    appInfo.appIsSemiFullScreen = 0;
    appInfo.appLaunchTime = procLifeRecord.processCreateTime;
    appInfo.appMemory = 0;
    appInfo.appPageFile = 0;
    appInfo.appName = procLifeRecord.processName;
    appInfo.appQuitTime = procLifeRecord.processCloseTime;
    std::string version;
    appInfo.appVersion = version;
    vecAppInfo.push_back(appInfo);
}

void AppUsingReport::FormatAppInfo(const PerfDataContainer& perfData, std::ostringstream& displayPerfDataStream)
{
    for (auto appinfo : perfData.vecApplicationInfo) {
        displayPerfDataStream << "{\"appName\":\"" << StringProc::WStringToStringByUTF8(appinfo.appName) <<
            "\",\"appVersion\":\"" << appinfo.appVersion << "\",\"appLaunchTime\":" <<
            static_cast<int>(appinfo.appLaunchTime) << ",\"appQuitTime\":" << appinfo.appQuitTime <<
            ",\"appPageFile\":" << appinfo.appPageFile <<
            ",\"appMemory\":" << appinfo.appMemory << ",\"appCpuRate\":" << appinfo.appCpuRate <<
            ",\"appDiskReadSpeepd\":" << appinfo.appDiskReadSpeepd << ",\"appDiskWriteSpeepd\":" <<
            appinfo.appDiskWriteSpeepd << ",\"appIsFocused\":" << appinfo.appIsFocused <<
            ",\"appIsFullScreen\":" << appinfo.appIsFullScreen << "},";
    }
}

void AppUsingReport::FormatAppCpuTopN(
    const std::vector<std::pair<std::string, std::pair<float, std::string>>>& cpuTopApp,
    std::string& outputInfo)
{
    std::ostringstream cpuTopAppStr;
    cpuTopAppStr << ",\"cpuTopApp\":[";
    for (auto& app : cpuTopApp) {
        cpuTopAppStr << "{\"" << app.first << "\":" << app.second.first <<
            ",\"appversion\":\"" << app.second.second << "\"},";
    }
    std::string outputstring = cpuTopAppStr.str();
    if (outputstring.size() > 1 && outputstring[outputstring.size() - 1] == ',') {
        outputstring.erase(outputstring.size() - 1, 1);
    }
    outputstring = outputstring + "]";
    outputInfo = outputstring;
}

void AppUsingReport::AppMemTopN(PerfDataContainer& perfData, std::string& topProcMemInfo)
{
    std::ostringstream memTopAppStr;
    memTopAppStr << ",\"memTopApp\":[";
    for (auto& app : perfData.vecTopProcMem) {
        memTopAppStr << "{\"" << app.first << "\":" << app.second << "},";
    }
    std::string outputstring = memTopAppStr.str();
    if (outputstring.size() > 1 && outputstring[outputstring.size() - 1] == ',') {
        outputstring.erase(outputstring.size() - 1, 1);
    }
    outputstring = outputstring + "]";
    topProcMemInfo = outputstring;
}

void AppUsingReport::AppPageTopN(PerfDataContainer& perfData, std::string& topProcPageInfo)
{
    std::ostringstream pageTopAppStr;
    pageTopAppStr << ",\"pageTopApp\":[";
    for (auto& app : perfData.vecTopProcPage) {
        pageTopAppStr << "{\"" << app.first << "\":" << app.second << "},";
    }
    std::string outputstring = pageTopAppStr.str();
    if (outputstring.size() > 1 && outputstring[outputstring.size() - 1] == ',') {
        outputstring.erase(outputstring.size() - 1, 1);
    }
    outputstring = outputstring + "]";
    topProcPageInfo = outputstring;
}

void AppUsingReport::GetSystemInfo(SystemInfo& systemInfo)
{
    int batteryLifePercent = 0;
    GetBatteryLifePercent(batteryLifePercent);
    systemInfo.batteryRemains = batteryLifePercent;
    systemInfo.bootTime = GetTickCount64() / DISCOUNT_BETWEEN_DAY_AND_MILLISECOND;
    systemInfo.cpuUsage = SysCpuCalc::GetInstance().GetSysCpuUsage();
    systemInfo.diskUsageRate = static_cast<float>(GetSystemDiskUsageRate());
    // 当前CPU频率
    systemInfo.cpuFreq = static_cast<float>(SysCpuCalc::GetInstance().GetCpuGhz());
    // 系统IO速度
    systemInfo.totalIoSpeed = static_cast<float>(PerfCounter::GetInstance().GetIOSpeed());
    unsigned long long totalProcess = 0;
    unsigned long long totalThread = 0;
    unsigned long long totalHandle = 0;
    PerfCounter::GetInstance().GetProcessThreadAndHandleInfo(totalProcess, totalThread, totalHandle);
    // 进程数量
    systemInfo.processNum = static_cast<size_t>(totalProcess);
    // 线程数量
    systemInfo.threadNum = static_cast<size_t>(totalThread);
    // 句柄数量
    systemInfo.handleNum = static_cast<size_t>(totalHandle);
    // cpu各个核心的使用率
    systemInfo.vecCpuCoreUsage = PerfCounter::GetInstance().GetCpuPerfCounterData();
    // gpu使用率
    systemInfo.gpuUsage = static_cast<float>(PerfCounter::GetInstance().GetSystemGpuUsage());
    systemInfo.isPowerConn = POWER_STATE_UNKNOW;
    int powerStatus = 0;
    if (GetPowerOnlineStatus(powerStatus)) {
        systemInfo.isPowerConn = powerStatus;
    }
    systemInfo.regFilesSize = 0;
    std::string systemResponseInfo = "[]";
    systemInfo.responseInfo = systemResponseInfo;
    systemInfo.currentTime = time(nullptr);
    systemInfo.currentTimeString = GetCurrentTimeWithMilliseconds();
    // 系统网络状态
    systemInfo.mode = 0;
    // SocNTC温度
    systemInfo.socNtcTemp = 0;
    // 内存信息
    GetSystemMemoryInfo(systemInfo);
}

bool AppUsingReport::GetFileVersion(const std::string& filePath, std::string& fileVersion)
{
    bool ret = false;
    BYTE* fileData = nullptr;
    do {
        if (filePath.empty()) {
            break;
        }
        DWORD temp = 0;
        DWORD size = GetFileVersionInfoSizeA(filePath.c_str(), &temp);
        if (size == 0) {
            break;
        }
        fileData = new (std::nothrow) BYTE[size + 1];
        if (fileData == nullptr) {
            break;
        }
        if (GetFileVersionInfoA(filePath.c_str(), 0, size, fileData) == FALSE) {
            break;
        }
        UINT len;
        VS_FIXEDFILEINFO* verInfo = nullptr;
        if (VerQueryValue(fileData, TEXT("\\"), reinterpret_cast<void**>(&verInfo), &len) == FALSE) {
            break;
        }
        DWORD verMS = verInfo->dwFileVersionMS;
        DWORD verLS = verInfo->dwFileVersionLS;
        DWORD major = HIWORD(verMS);
        DWORD minor = LOWORD(verMS);
        DWORD build = HIWORD(verLS);
        DWORD revision = LOWORD(verLS);
        char verTemp[MAX_PATH] = {0};
        if (sprintf_s(verTemp, MAX_PATH, "%u.%u.%u.%u", major, minor, build, revision) <= 0) {
            LOG_ERR("[DFX - AppUsingReport] - get file version failed.");
        }
        fileVersion = verTemp;
        ret = true;
    } while (false);
    if (fileData != nullptr) {
        delete[] fileData;
    }
    return ret;
}

void AppUsingReport::MonitorWinEvent()
{
    LOG_INFO("[DFX - AppUsingReport] - MonitorWinEvent start");
    do {
        SECURITY_ATTRIBUTES sa;
        if (!GetSecurityAttributes(sa)) {
            LOG_ERR("[DFX - AppUsingReport] - GetSrcurityAttributes failed!");
            break;
        }
        hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, BUFSIZE, BUFSIZE, 0, &sa);
        free(sa.lpSecurityDescriptor);
        sa.lpSecurityDescriptor = nullptr;
        if (hPipe == INVALID_HANDLE_VALUE) {
            LOG_ERR("[DFX - AppUsingReport] - CreateNamedPipe failed, err: {}", GetLastError());
            break;
        }
        (void)GuestUtils::ExecCmd("cmd /q /C \"taskkill /F /IM " + WIN_EVT_MONITOR_NAME + "\"", false);
        if (!GuestUtils::ExecCmd(WIN_EVT_MONITOR_PATH, true, false)) {
            LOG_ERR("[DFX - AppUsingReport] - Call win_evt_monitor failed");
        }
        connOverLapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
        if (!connOverLapped.hEvent) {
            LOG_ERR("Failed to create event for overlap while MonitorWinEvent");
            break;
        }
        connOverLapped.Offset = 0;
        connOverLapped.OffsetHigh = 0;
        LOG_INFO("[DFX - AppUsingReport] - wait for win_evt_monitor");
        isWinEvtMonitorConnected = false;
        BOOL ret = ConnectNamedPipe(hPipe, &connOverLapped);
        if (ret == FALSE) {
            if (GetLastError() != ERROR_IO_PENDING) {
                LOG_ERR("[DFX - AppUsingReport] - ConnectNamedPipe failed, err: {}", GetLastError());
                break;
            }
            if (isShutDown || stopMonitorWndEvtFlag ||
                WaitForSingleObject(connOverLapped.hEvent, INFINITE) == WAIT_FAILED) {
                LOG_ERR("[DFX - AppUsingReport] - ConnectNamedPipe failed, err: {}", GetLastError());
                break;
            }
        }
        isWinEvtMonitorConnected = true;
        RecvMessage(hPipe);
        DisconnectNamedPipe(hPipe);
    } while (false);
    SAFE_CLOSE_HANDLE(connOverLapped.hEvent, INVALID_HANDLE_VALUE);
    SAFE_CLOSE_HANDLE(hPipe, INVALID_HANDLE_VALUE);
    LOG_INFO("[DFX - AppUsingReport] - MonitorWinEvent end");
}

void AppUsingReport::RecvMessage(HANDLE hPipe)
{
    LOG_INFO("[DFX - AppUsingReport] - Begin recv msg from win_evt_monitor");
    while (true) {
        TCHAR *pchRequest = static_cast<TCHAR *>(malloc(BUFSIZE * sizeof TCHAR));
        if (pchRequest == nullptr) {
            LOG_ERR("[DFX - AppUsingReport] - pchRequest malloc failed");
            return;
        }
        OVERLAPPED overLapped;
        overLapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
        if (!overLapped.hEvent) {
            LOG_ERR("[DFX - AppUsingReport] - Failed to create event for overlap while recv");
            free(pchRequest);
            return;
        }
        overLapped.Offset = 0;
        overLapped.OffsetHigh = 0;
        DWORD dwBytesRead = 0;
        BOOL ret = ReadFile(hPipe, pchRequest, BUFSIZE * sizeof TCHAR, &dwBytesRead, &overLapped);
        if (!ret) {
            if (!isShutDown && !stopMonitorWndEvtFlag &&
                GetLastError() == ERROR_IO_PENDING) {
                ret = GetOverlappedResult(hPipe, &overLapped, &dwBytesRead, TRUE);
            }
        }
        if (!ret || dwBytesRead == 0) {
            LOG_ERR("[DFX - AppUsingReport] - ReadFile Failed, errCode: {}", GetLastError());
            free(pchRequest);
            SAFE_CLOSE_HANDLE(overLapped.hEvent, INVALID_HANDLE_VALUE);
            return;
        }
        std::string msg(pchRequest);
        free(pchRequest);
        SAFE_CLOSE_HANDLE(overLapped.hEvent, INVALID_HANDLE_VALUE);
        LOG_INFO("[DFX - AppUsingReport] Recv msg from monitor: {}", msg.c_str());
        if (msg.find("TaskBarStuck:") == 0) {
            // 任务栏卡死
            int prefixLen = 12; // 前缀长度 12
            std::string stuckMsg = msg.substr(prefixLen + 1, msg.length() - prefixLen).c_str();
        }
    }
}

BOOL AppUsingReport::GetSecurityAttributes(SECURITY_ATTRIBUTES& sa)
{
    sa.lpSecurityDescriptor = static_cast<PSECURITY_DESCRIPTOR>(malloc(SECURITY_DESCRIPTOR_MIN_LENGTH));
    if (sa.lpSecurityDescriptor == nullptr) {
        return FALSE;
    }
    InitializeSecurityDescriptor(sa.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, nullptr, FALSE);
    sa.nLength = sizeof(sa);
    sa.bInheritHandle = TRUE;
    return TRUE;
}

// 剩余电量的百分比,可以在0到100的范围内，如果状态未知，则可以是255
bool AppUsingReport::GetBatteryLifePercent(int& batteryLifePercent)
{
    SYSTEM_POWER_STATUS pwrStatus = { 0 };
    if (!GetSystemPowerStatus(&pwrStatus)) {
        LOG_ERR("PowerSlideControl::GetSystemPowerStatus failed: err = {}", GetLastError());
        return false;
    }
    batteryLifePercent = pwrStatus.BatteryLifePercent;
    return true;
}

double AppUsingReport::GetSystemDiskUsageRate()
{
    ULARGE_INTEGER freeBytesAvailableC = {0};
    ULARGE_INTEGER totalNumberOfBytesC = {0};
    ULARGE_INTEGER totalNumberOfFreeBytesC = {0};
    if (GetDiskFreeSpaceEx(_T("C:"), &freeBytesAvailableC, &totalNumberOfBytesC, &totalNumberOfFreeBytesC) == 0) {
        LOG_ERR("PerfCheckSysStates: GetDiskFreeSpaceEx() fail!");
        return -1;
    }

    if (totalNumberOfBytesC.QuadPart == 0) {
        LOG_ERR("totalNumberOfBytesC.QuadPart is 0.");
        return -1;
    }
    double diskUsage = 1 - static_cast<double>(totalNumberOfFreeBytesC.QuadPart) /
        static_cast<double>(totalNumberOfBytesC.QuadPart);
    return diskUsage;
}

// 0: DC  1:AC 255:未知状态
bool AppUsingReport::GetPowerOnlineStatus(int& powerStatus)
{
    SYSTEM_POWER_STATUS pwrStatus = { 0 };
    if (!GetSystemPowerStatus(&pwrStatus)) {
        LOG_ERR("PowerSlideControl::GetSystemPowerStatus failed: err = {}", GetLastError());
        return false;
    }
    powerStatus = pwrStatus.ACLineStatus;
    return true;
}

std::string AppUsingReport::GetCurrentTimeWithMilliseconds()
{
    const int currentTimeLength = 32;
    char currentTime[currentTimeLength] = { 0 }; // 指定当前时间最大长度
    SYSTEMTIME sys;
    GetLocalTime(&sys);
    if (sprintf_s(currentTime,
        currentTimeLength,
        "%4d%02d%02d%02d%02d%02d%03d",
        sys.wYear,
        sys.wMonth,
        sys.wDay,
        sys.wHour,
        sys.wMinute,
        sys.wSecond,
        sys.wMilliseconds) < 0) {
        LOG_ERR("sprintf_s GetCurrentTimeWithMilliseconds fail");
    }
    return std::string(currentTime);
}

void AppUsingReport::GetSystemMemoryInfo(SystemInfo& systemInfo)
{
    MEMORYSTATUSEX memoryStatusEx;
    SecureZeroMemory(&memoryStatusEx, sizeof(MEMORYSTATUSEX));
    memoryStatusEx.dwLength = sizeof(memoryStatusEx);
    if (GlobalMemoryStatusEx(&memoryStatusEx) != 0) {
        // 系统可用内存
        systemInfo.availPhyMem = memoryStatusEx.ullAvailPhys / BYTES_TO_KB;
        // 系统可用提交内存
        systemInfo.availPageFile = memoryStatusEx.ullAvailPageFile / BYTES_TO_KB;
        // 系统总内存
        systemInfo.totalPhyMem = memoryStatusEx.ullTotalPhys / BYTES_TO_KB;
        // 系统总提交内存
        systemInfo.totalPageFile = memoryStatusEx.ullTotalPageFile / BYTES_TO_KB;
        // 系统提交内存使用率
        if (memoryStatusEx.ullTotalPageFile != 0) {
            systemInfo.pageLoad = static_cast<int>(((memoryStatusEx.ullTotalPageFile -
                memoryStatusEx.ullAvailPageFile) * ONE_HUNDRED_PERCENT / memoryStatusEx.ullTotalPageFile));
        }
        systemInfo.memoryUsage = static_cast<int>(memoryStatusEx.dwMemoryLoad);
    } else {
        systemInfo.availPhyMem = 0;
        systemInfo.availPageFile = 0;
        systemInfo.totalPhyMem = 0;
        systemInfo.totalPageFile = 0;
    }
    // 内存空闲率
    systemInfo.memoryFreeRate = ONE_HUNDRED_PERCENT - systemInfo.memoryUsage;
}

void AppUsingReport::FormatSystemInfo(const PerfDataContainer &perfData, std::ostringstream& displayPerfDataStream)
{
    std::string cpuCorePerfData = "";
    for (auto cpuPerfData : perfData.systemInfo.vecCpuCoreUsage) {
        cpuCorePerfData += std::to_string(cpuPerfData);
        cpuCorePerfData += ",";
    }
    if (cpuCorePerfData.size() > 0 && cpuCorePerfData[cpuCorePerfData.size() - sizeof(char)] == ',') {
        // 去掉最后一个逗号
        cpuCorePerfData.erase(cpuCorePerfData.size() - 1, 1);
    }
    displayPerfDataStream << ",\"systemInfo\":{\"currentTime\":" << perfData.systemInfo.currentTime <<
        ",\"currentTimeString\":\"" << perfData.systemInfo.currentTimeString << "\",\"bootMemory\":" <<
        perfData.systemInfo.bootMemory << ",\"bootTime\":" << perfData.systemInfo.bootTime <<
        ",\"cpuUsage\":" << perfData.systemInfo.cpuUsage << ",\"memoryUsage\":" <<
        perfData.systemInfo.memoryUsage << ",\"cpuTemp\":" << perfData.systemInfo.cpuTemp <<
        ",\"isPowerConn\":" << perfData.systemInfo.isPowerConn << ",\"batteryRemains\":" <<
        perfData.systemInfo.batteryRemains << ",\"diskUsageRate\":" <<
        perfData.systemInfo.diskUsageRate << ",\"responseInfo\":" << perfData.systemInfo.responseInfo <<
        ",\"memoryFreeRate\":" << perfData.systemInfo.memoryFreeRate <<
        ",\"fileSystemCache\":" << perfData.systemInfo.fileSystemCache << ",\"totalIoSpeed\":" <<
        perfData.systemInfo.totalIoSpeed << ",\"handleNum\":" << perfData.systemInfo.handleNum <<
        ",\"processNum\":" << perfData.systemInfo.processNum << ",\"threadNum\":" <<
        perfData.systemInfo.threadNum << ",\"cpuFreq\":" << perfData.systemInfo.cpuFreq <<
        ",\"cpuPower\":" << perfData.systemInfo.cpuPower << ",\"mode\":" << perfData.systemInfo.mode <<
        ",\"socNtcTemp\":" << perfData.systemInfo.socNtcTemp <<",\"cpuCoreUsage\":[" << cpuCorePerfData <<
        "],\"gpuUsage\":" << perfData.systemInfo.gpuUsage << ",\"availPhyMem\":"
        << perfData.systemInfo.availPhyMem << ",\"totalPhyMem\":" << perfData.systemInfo.totalPhyMem <<
        ",\"powerLimit\":" << perfData.systemInfo.powerLimit <<
        ",\"availPageFile\":" << perfData.systemInfo.availPageFile << ",\"totalPageFile\":" <<
        perfData.systemInfo.totalPageFile << ",\"pageLoad\":" << perfData.systemInfo.pageLoad << "}";
}
}  // namespace DFX
